package com.ymt.bpm.service.engine.task.impl;

import com.alibaba.fastjson.JSONObject;
import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.engine.EngineConst;
import com.ymt.bpm.engine.bpmn.BpmnRender;
import com.ymt.bpm.engine.bpmn.ProcessUtil;
import com.ymt.bpm.model.*;
import com.ymt.bpm.service.engine.bpd.BpdService;
import com.ymt.bpm.service.engine.bpd.BpdVersionCacheService;
import com.ymt.bpm.service.engine.task.FmdIntegrationService;
import com.ymt.bpm.service.engine.task.PiService;
import com.ymt.bpm.util.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
 * Created by Johnny on 2017/4/17.
 */
@Service
public class PiServiceImpl implements PiService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Environment env;

    @Autowired
    private BpdVersionCacheService bvcs;

    @Autowired
    private BpdService bs;

    @Autowired
    private FmdIntegrationService fis;

    @Autowired
    private MDao dao;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private org.activiti.engine.TaskService taskService;

    @Autowired
    private RepositoryService reps;

    /*@Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject startProcess(String tenantId, String bpdId, String vId, String parentPiid, String userId, String userName, String title, JSONObject variables) {
        //process var
        JSONObject processVar = new JSONObject();

        JSONObject creator = new JSONObject();
        creator.put("userId", userId);
        creator.put("userName", userName);
        processVar.put("creator", creator);
        processVar.put("parentPiid", parentPiid);
        processVar.put("title", title);

        JSONObject version;
        boolean startByVid = false;
        if (vId==null || vId.length()==0) {
            //get current version and start by key
            version = bvcs.getCurrentVersionJson(tenantId, bpdId);
        } else {
            //get process deploy id by version from engine, start by id
            version = bvcs.getVersionJson(tenantId, bpdId, Integer.parseInt(vId));
            startByVid = true;
        }

        //生成流程变量
        Map<String, Object> startMap = new HashMap<String, Object>();
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");
        Map<String, Object> inputVars = variables!=null ? variables : null;
        JSONObject processVarVariables = generateProcessVarialbes(bpmnConfig, variables);
        processVar.put("variables", processVarVariables);
        startMap.put("processVar", processVar);

        //其他系统预置变量
        startMap.putAll(parsePredefinedSysVars(inputVars));

        ProcessInstance epi;
        if (!startByVid) {
            epi = runtimeService.startProcessInstanceByKeyAndTenantId(bpdId, startMap, tenantId);
        } else {
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put(Const.TENANT_ID, tenantId);
            paramMap.put("bpdId", bpdId);
            paramMap.put("versionid", version.getInteger("versionid"));
            String dId = dao.selectOne(Mapper.ActivitiMapper, "selectBpdDefIdByKeyVer", paramMap);
            epi = runtimeService.startProcessInstanceByIdAndTenantId(dId, null, startMap, tenantId);
        }

        JSONObject rtn = new JSONObject();
        rtn.put("piEid", epi.getId());
        return rtn;
    }*/

    @Override
    public JSONObject get(String tenantId, String piEid, String lang, int timezoneOffset) {
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(piEid);
        pi = dao.selectOne(Mapper.PPiMapper, "selectByObj", pi);
        return DhxUtil.toJsonWithDateAli(pi, lang, timezoneOffset);
    }

    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject statePiToHi(String tenantId, String loginName, String displayName, String piId, String newStatus, String deleteReason) {
        JSONObject rtn = new JSONObject();
        int rs = 0;

        //pis to his
        PPi key = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            key.setTenantId(Integer.parseInt(tenantId));
        }
        key.setPiId(piId);
        List<PPi> list = dao.selectListByObj(Mapper.PPiMapper, "selectByObj", key);
        List<PPiHi> listHi = new ArrayList<PPiHi>();
        Date now = DateUtil.getGMTDate();
        for (PPi pi : list) {
            pi.setStatus(newStatus);
            PPiHi hi = new PPiHi();
            hi.setTenantId(pi.getTenantId());
            hi.setPiEid(pi.getPiEid());
            hi.setPiId(pi.getPiId());
            hi.setVersionid(pi.getVersionid());
            hi.setBizId(pi.getBizId());
            hi.setBpdId(pi.getBpdId());
            hi.setBpdName(pi.getBpdName());
            hi.setUserId(pi.getUserId());
            hi.setUserName(pi.getUserName());
            hi.setParentPieid(pi.getParentPieid());
            hi.setStatus(pi.getStatus());
            hi.setCreateTime(pi.getCreateTime());
            hi.setCompleteDate(now);
            hi.setDueTime(pi.getDueTime());
            hi.setDeleteReason(deleteReason); //TODO
            hi.setDuration(new Long(now.getTime() - pi.getCreateTime().getTime()).intValue());
            //hi.setStartActId(); //TODO
            //hi.setEndActId();
            hi.setBizNo(pi.getBizNo());
            hi.setTitle(pi.getTitle());
            hi.setSummary(pi.getSummary());
            hi.setCallActivityId(pi.getCallActivityId());
            listHi.add(hi);

            //结束引擎流程，仅结束主流程
            /*if (pi.getParentPieid()==null || pi.getParentPieid().length()==0) {
                runtimeService.deleteProcessInstance(pi.getPiEid(), deleteReason);
            }*/
        }
        rs += dao.delete(Mapper.PPiMapper, "deleteByObj", key);
        rs += dao.batchInsert(Mapper.PPiHiMapper, "insertSelective", listHi);

        //todos to dones
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(Const.TENANT_ID, tenantId);
        paramMap.put("piId", piId);
        paramMap.put("completeDate", DateUtil.getGMTDate());
        paramMap.put("userId", loginName);
        paramMap.put("userName", displayName);
        rs += dao.insert(Mapper.ProcessMapper, "todoToDone", paramMap);
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setPiId(piId);
        rs += dao.delete(Mapper.ProcessMapper, "deleteTodoByObj", todo);

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 从草稿发起流程
     * @param tenantId
     * @param tt
     * @param bpmnConfig
     * @param params
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject startProcessByDraft(String tenantId, PTaskTodo tt, JSONObject bpmnConfig, JSONObject params) {
        int rs = 0;
        JSONObject pijo = params.getJSONObject("pi");
        String bpdId = pijo.getString("bpdId");
        int versionid = pijo.getInteger("versionid");
        boolean jsonDataEnabled = Boolean.parseBoolean(env.getProperty("bpmp.fmd-json-bizdata", "false"));

        //查找PBpdSeq
        Map<String, Object> bpdKeymap = new HashMap<String, Object>();
        bpdKeymap.put(Const.TENANT_ID, tenantId);
        bpdKeymap.put("bpdId", bpdId);
        PBpdSeq bpdSeq = dao.selectOne(Mapper.PBpdSeqMapper, "selectByPrimaryKey", bpdKeymap);

        //在引擎中发起 ==================== start
        // 生成流程变量
        Map<String, Object> startMap = new HashMap<String, Object>();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            startMap.put(Const.TENANT_ID, tenantId);
        }
        startMap.put(EngineConst.DRAFT_ACTIVITY, tt.getActivityId());
        startMap.put(EngineConst.PV_MAINPIID, tt.getPiId());
        startMap.put(EngineConst.PV_SKIP_TO, null);

        //生成流程变量，因为直接完成起草，这里不需要再给起草传递bizInput
        JSONObject variables = ProcessUtil.generateProcessVarialbes(bpmnConfig, null);
        JSONObject processVar = new JSONObject();
        processVar.put(EngineConst.VARIABLES, variables);
        startMap.put(EngineConst.PROCESSVAR, processVar);

        String dId = bvcs.selectBpdDefIdByKeyVer(tenantId, bpdId, versionid);
        ProcessInstance epi = runtimeService.startProcessInstanceByIdAndTenantId(dId, null, startMap, tenantId);
        Task etask = taskService.createTaskQuery().processInstanceId(epi.getId()).singleResult();

        //在引擎中发起 ==================== end

        String oldPiEid = pijo.getString("piEid");
        String newPiEid = epi.getId();
        String newTiEid = etask.getId();

        //更新TI -> DONE
//        tt.setTiEId(newTiEid);
//        tt.setPiEid(newPiEid);
//        tt.setPurpose(PTaskTodo.PURPOSE_NONE);
//        tt.setCreateTime(DateUtil.getGMTDate());
        //int rs = dao.update(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", tt);
        rs += dao.delete(Mapper.PTaskTodoMapper, "deleteByObj", tt);
        PTaskDone done = ProcessUtil.todoToDone(tt);
        //PComment comment = new PComment();
        Date now = DateUtil.getGMTDate();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            done.setTenantId(Integer.parseInt(tenantId));
            //comment.setTenantId(Integer.parseInt(tenantId));
        }
        done.setTiEId(newTiEid);
        done.setPiEid(newPiEid);
        done.setCreateTime(now);
        done.setReceiveTime(now);
        done.setCompleteDate(now);
        done.setOperation(params.getInteger("operation"));
        rs += dao.insert(Mapper.PTaskDoneMapper, "insert", done);
//        comment.setId(UUID.randomUUID().toString());
//        comment.setTiId(tt.getTiId());
//        comment.setPiId(tt.getPiId());
//        comment.setAddTime(now);
//        comment.setUserId(tt.getUserId());
//        comment.setUserName(tt.getUserName());
//        comment.setComment(params.getString("comments"));
//        rs += dao.insert(Mapper.PCommentMapper, "insert", comment);

        //设置流程到期时间
        Date dueTime = ProcessUtil.getPiDueTimeFromNow(bpmnConfig);

        //更新PI
        Map<String, Object> piUpdate = new HashMap<String, Object>();
        piUpdate.put(Const.TENANT_ID, tenantId);
        piUpdate.put("oldPiEid", oldPiEid);
        piUpdate.put("newPiEid", newPiEid);
        piUpdate.put("createTime", DateUtil.getGMTDate());
        piUpdate.put("dueTime", dueTime);
        piUpdate.put("status", PPi.PI_STATUS_ACTIVE);
        String newTitle = pijo.getString("title");
        if (newTitle!=null && newTitle.length()>0) {
            piUpdate.put("title", newTitle);
        }
        //获取单号
        int seqNo = dao.selectOne(Mapper.PBpdSeqMapper, "selectNextVal", bpdKeymap);
        String bizNo = BizNoUtil.generateBizNo(bpdSeq.getPattern(), seqNo);
        piUpdate.put("bizNo", bizNo);
        rs += dao.update(Mapper.PPiMapper, "updateByForStartProcess", piUpdate);
        //检查bizNo是否达到SEQ最大值
        rs += checkBizNoMax(bpdId, bpdSeq.getPattern(), seqNo);

        //更新PI UserRef，无需更新TODO UserRef，因为发起时仅peEid变化
        Map<String, Object> urUpdate = new HashMap<String, Object>();
        urUpdate.put(Const.TENANT_ID, tenantId);
        urUpdate.put("oldPiEid", oldPiEid);
        urUpdate.put("newPiEid", newPiEid);
        urUpdate.put("type", "0");
        urUpdate.put("userId", tt.getUserId());
        rs += dao.update(Mapper.PUserRefMapper, "updateByForStartProcess", urUpdate);

        //更新业务数据,更新业务数据TS_ID
        if (jsonDataEnabled) {
            StringBuilder updateSql = new StringBuilder("update P_DATA_M set PI_EID='")
                    .append(newPiEid)
                    .append("' where PI_EID='")
                    .append(oldPiEid)
                    .append("'");
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                updateSql.append(" and TENANT_ID=").append(tenantId);
            }
            Map<String, String> map = new HashMap<String, String>();
            map.put("sql", updateSql.toString());
            rs += dao.update(Mapper.CustomSQLMapper, "update", map);
        } else {
            PDraftTplRef ref = new PDraftTplRef();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                ref.setTenantId(Integer.parseInt(tenantId));
            }
            ref.setTiId(tt.getTiId());
            ref = dao.selectOne(Mapper.PDraftTplRefMapper, "selectByPrimaryKey", ref);
            JSONObject models = JSONObject.parseObject(ref.getModels());
            List<Map<String, String>> updates = new ArrayList<Map<String, String>>();
            for (String model: models.keySet()) {
                StringBuilder updateSql = new StringBuilder("update ")
                        .append(model)
                        .append(tenantId!=null?"_"+tenantId:"")
                        .append(" set TS_ID='")
                        .append(newPiEid)
                        .append("'")
                        .append(" where TS_ID='")
                        .append(oldPiEid)
                        .append("'");
                Map<String, String> map = new HashMap<String, String>();
                map.put("sql", updateSql.toString());
                updates.add(map);
            }
            rs += dao.batchUpdate(Mapper.CustomSQLMapper, "update", updates);
        }

        //记录授权信息
        PPiAuth piAuth = new PPiAuth();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            piAuth.setTenantId(Integer.parseInt(tenantId));
        }
        piAuth.setPiId(tt.getPiId());
        piAuth.setUserId(tt.getUserId());
        piAuth.setAuthType(PPiAuth.AUTH_TYPE_CREATOR);
        rs += dao.insert(Mapper.PPiAuthMapper, "insertOrUpdate", piAuth);

        //complete the draft activity
        //bizOutput to processVar
        JSONObject bizOutput = params.getJSONObject("bizOutput");
        ProcessUtil.mappingBizVarToProcessVar(tt.getActivityId(), bpmnConfig, processVar, bizOutput);
        ProcessUtil.calculateGatewayVar(tt.getActivityId(), bpmnConfig, processVar);
        Map<String, Object> completeArg = new HashMap<String, Object>();
        completeArg.put(EngineConst.PROCESSVAR, processVar);
        taskService.complete(etask.getId(), completeArg);

        JSONObject rtn = new JSONObject();
        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject statePi(String tenantId, String loginName, String displayName, String piId, String newStatus) {
        JSONObject rtn = new JSONObject();
        int rs = 0;
        PPi key = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            key.setTenantId(Integer.parseInt(tenantId));
        }
        key.setPiId(piId);
        List<PPi> list = dao.selectListByObj(Mapper.PPiMapper, "selectByObj", key);
        for (PPi pi : list) {
            pi.setStatus(newStatus);
        }
        rs += dao.batchUpdate(Mapper.PPiMapper, "updateByPrimaryKeySelective", list);
        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject stateHi(String tenantId, String loginName, String displayName, String piId, String newStatus) {
        JSONObject rtn = new JSONObject();
        int rs = 0;
        PPiHi key = new PPiHi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            key.setTenantId(Integer.parseInt(tenantId));
        }
        key.setPiId(piId);
        List<PPiHi> list = dao.selectListByObj(Mapper.PPiHiMapper, "selectByObj", key);
        for (PPiHi pi : list) {
            pi.setStatus(newStatus);
        }
        rs += dao.batchUpdate(Mapper.PPiHiMapper, "updateByPrimaryKeySelective", list);
        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 起草
     * @param tenantId
     * @param userId
     * @param userName
     * @param bpdId
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject startDraft(String tenantId, String userId, String userName, String bpdId) {

        JSONObject rtn = new JSONObject();

        //get bpd info
        PBpd bpd = bvcs.getBasicBean(tenantId, bpdId);
        int versionid = bs.getCurrentVersionId(tenantId, bpdId);
        JSONObject version = bvcs.getVersionJson(tenantId, bpdId, versionid);
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");

        //get first activity config
        String startEvent = bpmnConfig.getJSONObject("Participant_1").getJSONObject("layout").getString("startEvent");
        String activityId = bpmnConfig.getJSONObject(startEvent).getJSONObject("layout").getString("targetRef");
        JSONObject activityConf = bpmnConfig.getJSONObject(activityId);
        if (!"UserTask".equalsIgnoreCase(activityConf.getString("i18ntype"))) {
            rtn.put("msg", "FIRST_NOT_USERTASK");   //first activity is not an user task
            return rtn;
        }

        //get user dept info
        Map<String, Object> deptArgMap = new HashMap<String, Object>();
        deptArgMap.put(Const.TENANT_ID, tenantId);
        deptArgMap.put("userId", userId);
        POrg org = dao.selectOne(Mapper.POrgMapper, "selectMainOrgOfUser", deptArgMap);

        //create pi
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(UUID.randomUUID().toString());
        pi.setPiId("PID-"+UUID.randomUUID().toString());
        pi.setVersionid(version.getInteger("versionid"));
        pi.setBpdId(bpdId);
        pi.setBpdName(bpd.getBpdName());
        pi.setUserId(userId);
        pi.setUserName(userName);
        pi.setStatus("-1");
        pi.setCreateTime(DateUtil.getGMTDate());
        //pi.setBizId(null);
//        pi.setDueTime(null);
//        pi.setBizNo(null);
//        pi.setTitle(null);
//        pi.setSummary(null);
//        pi.setParentPieid(null);
//        pi.setCallActivityId(null);

        PUserRef piUserRef = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            piUserRef.setTenantId(Integer.parseInt(tenantId));
        }
        piUserRef.setId(pi.getPiEid());
        piUserRef.setType(PUserRef.TYPE_PROCESS);
        piUserRef.setUserId(userId);
        piUserRef.setUserName(userName);
        piUserRef.setDeptId(org.getOrgId());
        piUserRef.setDeptName(org.getOrgName());//TODO role info

        //create ti
        PTaskTodo ti = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            ti.setTenantId(Integer.parseInt(tenantId));
        }
        ti.setTiId("TID-"+UUID.randomUUID());
        ti.setTiEId(UUID.randomUUID().toString());
        ti.setPiId(pi.getPiId());
        ti.setPiEid(pi.getPiEid());
        ti.settType("0");
        ti.setPurpose(PTaskTodo.PURPOSE_DRAFT);
        ti.setActivityId(activityId);
        ti.setActivityName(activityConf.getString("i18nname-zh"));
        ti.setPriority(0);
        ti.setUserId(userId);
        ti.setUserName(userName);
        ti.setStatus("00");
        ti.setCreateTime(DateUtil.getGMTDate());
        //TODO priority from activityConf
        ti.setPriority(PTaskTodo.PRIORITY_NORMAL);

//        ti.setReceiveTime(receiveTime);
//        ti.setDueTime(dueTime);
//        ti.setSummary(summary);

        PUserRef tiUserRef = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            tiUserRef.setTenantId(Integer.parseInt(tenantId));
        }
        tiUserRef.setId(ti.getTiId());
        tiUserRef.setType(PUserRef.TYPE_TASK);
        tiUserRef.setUserId(userId);
        tiUserRef.setUserName(userName);
        tiUserRef.setDeptId(org.getOrgId());
        tiUserRef.setDeptName(org.getOrgName());//TODO role info

        int rs = 0;
        rs += dao.insert(Mapper.PPiMapper, "insertSelective", pi);
        rs += dao.insert(Mapper.PUserRefMapper, "insertSelective", piUserRef);
        rs += dao.insert(Mapper.PTaskTodoMapper, "insertSelective", ti);
        rs += dao.insert(Mapper.PUserRefMapper, "insertSelective", tiUserRef);

        //save biz models ref
        //校验form file
        JSONObject formBinding = activityConf.getJSONObject("formBinding");
        JSONObject models = fis.getModelsOfForm(tenantId, formBinding.getString("formid"), formBinding.getString("versionid"));
        PDraftTplRef ref = new PDraftTplRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            ref.setTenantId(Integer.parseInt(tenantId));
        }
        ref.setTiId(ti.getTiId());
        ref.setModels(models.toJSONString());
        rs += dao.insert(Mapper.PDraftTplRefMapper, "insert", ref);

        rtn.put("tiId", ti.getTiId());
        rtn.put("piId", pi.getPiId());  //add for benchmark testing
        rtn.put("piEid", pi.getPiEid());  //add for benchmark testing
        rtn.put("result", rs);

        //create task
        return rtn;
    }

    @Override
    public JSONObject getByCallActivityId(String tenantId, String piId, String parentPiEid, String callActivityId, String lang, int timezoneOffset) {
        PPi pi = new PPi();
        if (tenantId!=null && tenantId.length()>0 && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiId(piId);
        pi.setParentPieid(parentPiEid);
        pi.setCallActivityId(callActivityId);
        pi = dao.selectOne(Mapper.PPiMapper, "selectByObj", pi);
        return DhxUtil.toJsonWithDateAli(pi, lang, timezoneOffset);
    }

    /**
     * 获取动态调整需要的信息
     * @param tenantId
     * @param piEid
     * @param lang
     * @param timezoneOffset
     * @return
     */
    @Override
    public JSONObject infoForChange(String tenantId, String piEid, String lang, int timezoneOffset) {
        JSONObject rtn = new JSONObject();
        PTaskTodo todoKey = new PTaskTodo();
        PTaskDone doneKey = new PTaskDone();
        PSeqflow sfKey = new PSeqflow();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            int tnid = Integer.parseInt(tenantId);
            todoKey.setTenantId(tnid);
            doneKey.setTenantId(tnid);
            sfKey.setTenantId(tnid);
        }
        todoKey.setPiEid(piEid);
        doneKey.setPiEid(piEid);
        sfKey.setPiEid(piEid);
        List<PTaskTodo> todoList = dao.selectObjListByObj(Mapper.PTaskTodoMapper, "selectByObj", todoKey);
        List<PTaskDone> doneList = dao.selectObjListByObj(Mapper.PTaskDoneMapper, "selectByObj", doneKey);
        List<PSeqflow> seqflows = dao.selectObjListByObj(Mapper.PSeqflowMapper, "selectByObj", sfKey);
        JSONObject todos = new JSONObject();
        if (todoList!=null && todoList.size()>0) {
            for (PTaskTodo todo : todoList) {
                todos.put(todo.getActivityId(), true);
            }
        }
        rtn.put("todos", todos);
        JSONObject dones = new JSONObject();
        if (doneList!=null && doneList.size()>0) {
            for (PTaskDone done : doneList) {
                dones.put(done.getActivityId(), true);
            }
        }
        rtn.put("dones", dones);
        rtn.put("seqflows", seqflows);
        return rtn;
    }

    /**
     * 执行动态调整
     * @param tenantId
     * @param loginName
     * @param displayName
     * @param piEid
     * @param param
     * @return
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws XPathExpressionException
     * @throws IOException
     * @throws TransformerException
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject saveDynamicChange(String tenantId,
                                        String loginName, String displayName,
                                        String piEid, String param)
            throws ParserConfigurationException, SAXException, XPathExpressionException, IOException, TransformerException {
        JSONObject rtn = new JSONObject();
        int rs = 0;
        JSONObject paramJo = JSONObject.parseObject(param);
        String changedBpdId = "INST-"+ piEid;
        String bpmn = paramJo.getString("bpmn");
        String svg = paramJo.getString("svg");
        String bpmnConfig = paramJo.getString(EngineConst.BPMNCONF);
        String otherConfig = paramJo.getString(EngineConst.OTHERCONF);

        //获取pi
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(piEid);
        pi = dao.selectOne(Mapper.PPiMapper, "selectByObj", pi);

        //获取bpd信息
        String bpdId = pi.getBpdId();
        PBpdWithBLOBs bpd = null;   //bpd
        Map<String, Object> bpdKeyMap = new HashMap<String, Object>();
        bpdKeyMap.put(Const.TENANT_ID, tenantId);
        if (!bpdId.equalsIgnoreCase(changedBpdId)) {//未动态调整过，复制流程
            bpdKeyMap.put("bpdId", bpdId);
            bpd = dao.selectOne(Mapper.PBpdMapper, "selectByPrimaryKey", bpdKeyMap);
            bpd.setBpdId(changedBpdId);
            bpd.setBpdDesc(bpd.getBpdDesc()+" Copy.");
            bpd.setBpmn(bpmn);
            bpd.setSvg(svg);
            bpd.setBpmnConfig(bpmnConfig);
            bpd.setOtherConfig(otherConfig);
            bpd.setStatus("10");
            bpd.setIsdynamic("1");
            rs += dao.insert(Mapper.PBpdMapper, "insert", bpd);
        } else {
            bpdKeyMap.put("bpdId", bpdId);
            bpd = dao.selectOne(Mapper.PBpdMapper, "selectByPrimaryKey", bpdKeyMap);
        }

        //部署新版本
        bpdKeyMap.put("bpdId", changedBpdId);
        int currentVersionId = bs.getCurrentVersionId(tenantId, changedBpdId);
        int newVersionId = currentVersionId+1;
        PBpdVersionWithBLOBs version = new PBpdVersionWithBLOBs();
        version.setBpdId(changedBpdId);
        version.setVersionid(newVersionId);
        // version.setVersionname(bpd.getBpdName()+" Version:"+newVersionId);
        // version.setVersiondesc(bpd.getBpdName()+" Version:"+newVersionId);
        version.setCreator(loginName);
        version.setCreatorName(displayName);
        version.setCreateTime(DateUtil.getGMTDate());
        version.setBpmn(bpmn);
        version.setBpmnConfig(bpmnConfig);
        version.setOtherConfig(otherConfig);
        version.setSvg(svg);
//        version.setBpmn1();
//        version.setBpmnConfig1();
//        version.setOtherConfig1();
//        version.setSvg1();
        //新部署的版本，默认为当前活动版本
        version.setIsCurrent(1);
        //将原活动版本置为未激活状态
        rs += dao.insert(Mapper.PBpdVersionMapper, "insert", version);
        //设置部署前的当前版本为0
        rs += dao.update(Mapper.PBpdVersionMapper, "setNoCurrent", bpdKeyMap);

        //引擎部署 //转换为可部署bpmn xml
        BpmnRender render = new BpmnRender(changedBpdId, bpd.getBpdName(), newVersionId, bpmn, null, bpmnConfig);
        String bpmnt = render.getTargetDocumentString();
        log.debug("instance deploying:\n" + bpmnt);
        reps.createDeployment()
                .name(bpd.getBpdName())
                .addInputStream(changedBpdId + ".bpmn", new ByteArrayInputStream(bpmnt.getBytes(Const.ENCODING)))
                .deploy();

        //更新引擎关联 //TODO tenantId test
        Map<String, Object> apMap = new HashMap<String, Object>();
        bpdKeyMap.put(Const.TENANT_ID, tenantId);
        bpdKeyMap.put("bpdId", changedBpdId);
        bpdKeyMap.put("piEid", piEid);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_RU_EXECUTION", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_RU_TASK", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_RU_EVENT_SUBSCR", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_RU_IDENTITYLINK", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_HI_TASKINST", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_HI_PROCINST", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_HI_ACTINST", apMap);
        rs += dao.update(Mapper.ActivitiMapper, "updateActBpdDefId_ACT_EVT_LOG", apMap);

        //更新pi bpdId versionid
        pi.setBpdId(changedBpdId);
        pi.setVersionid(newVersionId);
        rs += dao.update(Mapper.PPiMapper, "updateByPrimaryKeySelective", pi);

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    private int checkBizNoMax(String bpdId, String pattern, int seqNo) {
        String seqLen = pattern.substring(pattern.indexOf("{SEQ")+4);
        seqLen = seqLen.substring(0, seqLen.indexOf("}"));
        Long seqLength = new Double(Math.pow(10, Integer.parseInt(seqLen))).longValue() -1L;
        if (seqLength.intValue()==seqNo) {  //达到最大值
            PBpdSeq seq = new PBpdSeq();
            seq.setBpdId(bpdId);
            seq.setCurrentValue(1);
            return dao.update(Mapper.PBpdSeqMapper, "updateByPrimaryKeySelective", seq);
        }
        return 0;
    }

}
